/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "libzlang_msoffice.h"

struct MsWordLine
{
	uint32_t		text_buf_size ;
	uint32_t		text_len ;
	char			*text ;
	
	char			*style_id ;
} ;

struct MsWordStyle
{
	char			*style_id ;
	char			*style_name ;
	
	struct hlist_node	style_hashnode ;
} ;

#define MSWORDLINE_COUNT_INCREASEMENT		100

struct ZlangDirectProperty_msword
{
	struct ZlangRuntime	*rt ;
	
	char			*msword_filename ;
	unzFile			unzip_file ;
	
	char			*file_encoding ;
	char			*convert_encoding ;
	
	uint32_t		line_array_size ;
	struct MsWordLine	*line_array ;
	uint32_t		line_count ;
	struct MsWordLine	*line_ptr ;
	
	uint64_t		styles_hashmap_size ;
	struct hlist_head	*styles_hashmap ;
	struct MsWordStyle	*style ;
} ;

static void DestroyStyle( struct MsWordStyle *style )
{
	if( style )
	{
		if( style->style_id )
			free( style->style_id );
		if( style->style_name )
			free( style->style_name );
		
		free( style );
	}
	
	return;
}

int CreateStylesHashMap( struct ZlangDirectProperty_msword *msword_direct_prop );
void DestroyStylesHashMap( struct ZlangDirectProperty_msword *msword_direct_prop );
int LinkStylesHashNode_BY_style_id( struct ZlangDirectProperty_msword *msword_direct_prop , struct MsWordStyle *style );
struct MsWordStyle *QueryStylesHashNode_BY_style_id( struct ZlangDirectProperty_msword *msword_direct_prop , struct MsWordStyle *style );

CREATE_HLIST_ARRAY( CreateStylesHashMap , struct ZlangDirectProperty_msword , styles_hashmap , styles_hashmap_size )
DESTROY_HLIST_ARRAY( DestroyStylesHashMap , struct ZlangDirectProperty_msword , styles_hashmap , styles_hashmap_size , struct MsWordStyle , style_hashnode , DestroyStyle )
LINK_HLISTNODE_STRING( LinkStylesHashNode_BY_style_id , struct ZlangDirectProperty_msword , styles_hashmap , styles_hashmap_size , struct MsWordStyle , style_hashnode , style_id )
QUERY_HLISTNODE_STRING( QueryStylesHashNode_BY_style_id , struct ZlangDirectProperty_msword , styles_hashmap , styles_hashmap_size , struct MsWordStyle , style_hashnode , style_id )

#define XPATH_XML_ENCODING				"/xml.encoding"
#define XPATH_WDOCUMENT_WBODY_WP_WPPR_WPSTYLE_WVAL	"/w:document/w:body/w:p/w:pPr/w:pStyle.w:val"

static int CallbackOnDocumentXmlProperty( int type , char *xpath , int xpath_len , int xpath_size , char *propname , int propname_len , char *propvalue , int propvalue_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = (struct ZlangDirectProperty_msword *) user ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "XML - CallbackOnSheetXmlProperty - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( xpath_len == sizeof(XPATH_XML_ENCODING)-1 && STRNCMP( xpath , == , XPATH_XML_ENCODING , sizeof(XPATH_XML_ENCODING)-1 ) )
	{
		msword_direct_prop->file_encoding = strndup( propvalue , propvalue_len ) ;
		if( msword_direct_prop->file_encoding == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "malloc failed , errno[%d]" , errno )
			return -1;
		}
		TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "get file encoding[%s]" , msword_direct_prop->file_encoding )
	}
	else if( xpath_len == sizeof(XPATH_WDOCUMENT_WBODY_WP_WPPR_WPSTYLE_WVAL)-1 && STRNCMP( xpath , == , XPATH_WDOCUMENT_WBODY_WP_WPPR_WPSTYLE_WVAL , sizeof(XPATH_WDOCUMENT_WBODY_WP_WPPR_WPSTYLE_WVAL)-1 ) )
	{
		if( msword_direct_prop->line_ptr == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "msword_direct_prop->line_ptr is null" )
			return -1;
		}
		
		msword_direct_prop->line_ptr->style_id = strndup( propvalue , propvalue_len ) ;
		if( msword_direct_prop->line_ptr->style_id == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "malloc failed , errno[%d]" , errno )
			return -1;
		}
		TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "style_id[%s]" , msword_direct_prop->line_ptr->style_id )
	}
	
	return 0;
}

#define XPATH_XML				"/xml"
#define XPATH_WDOCUMENT_WBODY_WP		"/w:document/w:body/w:p"
#define XPATH_WDOCUMENT_WBODY_WP_WPPR_WPSTYLE	"/w:document/w:body/w:p/w:pPr/w:pStyle"
#define XPATH_WDOCUMENT_WBODY_WP_WR_WTAB	"/w:document/w:body/w:p/w:r/w:tab"
#define XPATH_WDOCUMENT_WBODY_WP_WR_WT		"/w:document/w:body/w:p/w:r/w:t"

static int CallbackOnDocumentXmlNode( int type , char *xpath , int xpath_len , int xpath_size , char *node , int node_len , char *tag , int tag_len , char *properties , int properties_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = (struct ZlangDirectProperty_msword *) user ;
	uint32_t				len ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "XML - CallbackOnDocumentXmlNode - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_XML)-1 && STRNCMP( xpath , == , XPATH_XML , sizeof(XPATH_XML)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnDocumentXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( ( type == (FASTERXML_NODE_ENTER|FASTERXML_NODE_BRANCH) || type == FASTERXML_NODE_BRANCH ) && xpath_len == sizeof(XPATH_WDOCUMENT_WBODY_WP)-1 && STRNCMP( xpath , == , XPATH_WDOCUMENT_WBODY_WP , sizeof(XPATH_WDOCUMENT_WBODY_WP)-1 ) )
	{
		if( msword_direct_prop->line_array == NULL || msword_direct_prop->line_count+1 >= msword_direct_prop->line_array_size )
		{
			struct MsWordLine	*tmp = NULL ;
			uint64_t		new_line_array_size ;
			
			new_line_array_size = msword_direct_prop->line_array_size + MSWORDLINE_COUNT_INCREASEMENT ;
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "line_array[%p] line_count[%"PRIu32"] line_array_size[%"PRIu32"]" , msword_direct_prop->line_array , msword_direct_prop->line_count , msword_direct_prop->line_array_size )
			tmp = (struct MsWordLine *)realloc( msword_direct_prop->line_array , sizeof(struct MsWordLine) * new_line_array_size ) ;
			if( tmp == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "realloc failed , errno[%d]" , errno )
				return -1;
			}
			msword_direct_prop->line_array = tmp ;
			msword_direct_prop->line_array_size = new_line_array_size ;
			if( msword_direct_prop->line_count == 0 )
			{
				memset( msword_direct_prop->line_array , 0x00 , sizeof(struct MsWordLine) * msword_direct_prop->line_array_size );
				msword_direct_prop->line_count = 0 ;
				msword_direct_prop->line_ptr = msword_direct_prop->line_array ;
			}
			else
			{
				msword_direct_prop->line_count++;
				msword_direct_prop->line_ptr = msword_direct_prop->line_array+msword_direct_prop->line_count ;
				memset( msword_direct_prop->line_ptr , 0x00 , sizeof(struct MsWordLine) * (msword_direct_prop->line_array_size-msword_direct_prop->line_count) );
			}
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "realloc line_ptr[%p] line_count[%"PRIu32"] line_array_size[%"PRIu32"]" , msword_direct_prop->line_ptr , msword_direct_prop->line_count , msword_direct_prop->line_array_size )
		}
		else
		{
			msword_direct_prop->line_ptr++;
			msword_direct_prop->line_count++;
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "line_ptr[%p] line_count[%"PRIu32"] line_array_size[%"PRIu32"]" , msword_direct_prop->line_ptr , msword_direct_prop->line_count , msword_direct_prop->line_array_size )
		}
	}
	else if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_WDOCUMENT_WBODY_WP_WPPR_WPSTYLE)-1 && STRNCMP( xpath , == , XPATH_WDOCUMENT_WBODY_WP_WPPR_WPSTYLE , sizeof(XPATH_WDOCUMENT_WBODY_WP_WPPR_WPSTYLE)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnDocumentXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_WDOCUMENT_WBODY_WP_WR_WTAB)-1 && STRNCMP( xpath , == , XPATH_WDOCUMENT_WBODY_WP_WR_WTAB , sizeof(XPATH_WDOCUMENT_WBODY_WP_WR_WTAB)-1 ) )
	{
		len = 1 ;
		
		if( msword_direct_prop->line_ptr->text == NULL || msword_direct_prop->line_ptr->text_len+len >= msword_direct_prop->line_ptr->text_buf_size )
		{
			char		*tmp = NULL ;
			uint64_t	new_text_buf_size ;
			
			new_text_buf_size = msword_direct_prop->line_ptr->text_len+len + 1 ;
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "realloc text[%p] text_len[%"PRIu32"]+len[%"PRIu32"] text_buf_size[%"PRIu32"]" , msword_direct_prop->line_ptr->text , msword_direct_prop->line_ptr->text_len , len , msword_direct_prop->line_ptr->text_buf_size)
			tmp = (char*)realloc( msword_direct_prop->line_ptr->text , new_text_buf_size ) ;
			if( tmp == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "realloc failed , errno[%d]" , errno )
				return -1;
			}
			msword_direct_prop->line_ptr->text = tmp ;
			msword_direct_prop->line_ptr->text_buf_size = new_text_buf_size ;
			memset( msword_direct_prop->line_ptr->text+msword_direct_prop->line_ptr->text_len , 0x00 , msword_direct_prop->line_ptr->text_buf_size-1-msword_direct_prop->line_ptr->text_len );
		}
		memcpy( msword_direct_prop->line_ptr->text+msword_direct_prop->line_ptr->text_len , "\t" , len );
		msword_direct_prop->line_ptr->text_len += len ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "text[%p] text_len[%"PRIu32"] text_buf_size[%"PRIu32"]" , msword_direct_prop->line_ptr->text , msword_direct_prop->line_ptr->text_len , msword_direct_prop->line_ptr->text_buf_size)
	}
	else if( type == FASTERXML_NODE_LEAF && xpath_len == sizeof(XPATH_WDOCUMENT_WBODY_WP_WR_WT)-1 && STRNCMP( xpath , == , XPATH_WDOCUMENT_WBODY_WP_WR_WT , sizeof(XPATH_WDOCUMENT_WBODY_WP_WR_WT)-1 ) )
	{
		len = content_len ;
		
		if( msword_direct_prop->line_ptr->text == NULL || msword_direct_prop->line_ptr->text_len+len >= msword_direct_prop->line_ptr->text_buf_size )
		{
			char		*tmp = NULL ;
			uint64_t	new_text_buf_size ;
			
			new_text_buf_size = msword_direct_prop->line_ptr->text_len+len + 1 ;
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "realloc text[%p] text_len[%"PRIu32"]+len[%"PRIu32"] text_buf_size[%"PRIu32"]" , msword_direct_prop->line_ptr->text , msword_direct_prop->line_ptr->text_len , len , msword_direct_prop->line_ptr->text_buf_size)
			tmp = (char*)realloc( msword_direct_prop->line_ptr->text , new_text_buf_size ) ;
			if( tmp == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "realloc failed , errno[%d]" , errno )
				return -1;
			}
			msword_direct_prop->line_ptr->text = tmp ;
			msword_direct_prop->line_ptr->text_buf_size = new_text_buf_size ;
			memset( msword_direct_prop->line_ptr->text+msword_direct_prop->line_ptr->text_len , 0x00 , msword_direct_prop->line_ptr->text_buf_size-1-msword_direct_prop->line_ptr->text_len );
		}
		memcpy( msword_direct_prop->line_ptr->text+msword_direct_prop->line_ptr->text_len , content , content_len );
		msword_direct_prop->line_ptr->text_len += content_len ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "text[%p] text_len[%"PRIu32"] text_buf_size[%"PRIu32"]" , msword_direct_prop->line_ptr->text , msword_direct_prop->line_ptr->text_len , msword_direct_prop->line_ptr->text_buf_size)
	}
	
	return 0;
}

static int ParseDocumentFileContent( struct ZlangDirectProperty_msword *msword_direct_prop , char *file_content , uint64_t file_content_len )
{
	char		xpath[ 1024 ] ;
	int		nret = 0 ;
	
	memset( xpath , 0x00 , sizeof(xpath) );
	nret = TravelXmlBuffer( file_content , xpath , sizeof(xpath) , CallbackOnDocumentXmlNode , msword_direct_prop ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "TravelXmlBuffer failed[%d]" , nret )
		return nret;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "TravelXmlBuffer ok" )
		return 0;
	}
}

#define XPATH_WSTYLES_WDOCDEFAULTS_WRPRDEFAULT_WRPR_WSZ_WVAL	"/w:styles/w:docDefaults/w:rPrDefault/w:rPr/w:sz.w:val"
#define XPATH_WSTYLES_WSTYLE_WSTYLEID				"/w:styles/w:style.w:styleId"
#define XPATH_WSTYLES_WSTYLE_WNAME_WVAL				"/w:styles/w:style/w:name.w:val"

static int CallbackOnStylesXmlProperty( int type , char *xpath , int xpath_len , int xpath_size , char *propname , int propname_len , char *propvalue , int propvalue_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = (struct ZlangDirectProperty_msword *) user ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "XML - CallbackOnStylesXmlProperty - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( xpath_len == sizeof(XPATH_WSTYLES_WDOCDEFAULTS_WRPRDEFAULT_WRPR_WSZ_WVAL)-1 && STRNCMP( xpath , == , XPATH_WSTYLES_WDOCDEFAULTS_WRPRDEFAULT_WRPR_WSZ_WVAL , sizeof(XPATH_WSTYLES_WDOCDEFAULTS_WRPRDEFAULT_WRPR_WSZ_WVAL)-1 ) )
	{
		msword_direct_prop->styles_hashmap_size = atol(propvalue) * 2 ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "styles_hashmap_size[%"PRIu64"]" , msword_direct_prop->styles_hashmap_size )
		nret = CreateStylesHashMap( msword_direct_prop ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "CreateStylesHashMap failed , errno[%d]" , nret )
			return -1;
		}
	}
	else if( xpath_len == sizeof(XPATH_WSTYLES_WSTYLE_WSTYLEID)-1 && STRNCMP( xpath , == , XPATH_WSTYLES_WSTYLE_WSTYLEID , sizeof(XPATH_WSTYLES_WSTYLE_WSTYLEID)-1 ) )
	{
		if( msword_direct_prop->style == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "msword_direct_prop->style is null" )
			return -1;
		}
		
		msword_direct_prop->style->style_id = strndup( propvalue , propvalue_len ) ;
		if( msword_direct_prop->style->style_id == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "strndup failed , errno[%d]" , nret )
			return -1;
		}
	}
	else if( xpath_len == sizeof(XPATH_WSTYLES_WSTYLE_WNAME_WVAL)-1 && STRNCMP( xpath , == , XPATH_WSTYLES_WSTYLE_WNAME_WVAL , sizeof(XPATH_WSTYLES_WSTYLE_WNAME_WVAL)-1 ) )
	{
		if( msword_direct_prop->style == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "msword_direct_prop->style is null" )
			return -1;
		}
		
		msword_direct_prop->style->style_name = strndup( propvalue , propvalue_len ) ;
		if( msword_direct_prop->style->style_name == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "strndup failed , errno[%d]" , nret )
			return -1;
		}
	}
	
	return 0;
}

#define XPATH_WSTYLES_WDOCDEFAULTS_WRPRDEFAULT_WRPR_WSZ		"/w:styles/w:docDefaults/w:rPrDefault/w:rPr/w:sz"
#define XPATH_WSTYLES_WSTYLE					"/w:styles/w:style"
#define XPATH_WSTYLES_WSTYLE_WNAME				"/w:styles/w:style/w:name"

static int CallbackOnStylesXmlNode( int type , char *xpath , int xpath_len , int xpath_size , char *node , int node_len , char *tag , int tag_len , char *properties , int properties_len , char *content , int content_len , void *user )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = (struct ZlangDirectProperty_msword *) user ;
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "XML - CallbackOnDocumentXmlNode - type[0x%X] xpath[%.*s]" , type , xpath_len,xpath )
	
	if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_WSTYLES_WDOCDEFAULTS_WRPRDEFAULT_WRPR_WSZ)-1 && STRNCMP( xpath , == , XPATH_WSTYLES_WDOCDEFAULTS_WRPRDEFAULT_WRPR_WSZ , sizeof(XPATH_WSTYLES_WDOCDEFAULTS_WRPRDEFAULT_WRPR_WSZ)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnStylesXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == (FASTERXML_NODE_ENTER|FASTERXML_NODE_BRANCH) && xpath_len == sizeof(XPATH_WSTYLES_WSTYLE)-1 && STRNCMP( xpath , == , XPATH_WSTYLES_WSTYLE , sizeof(XPATH_WSTYLES_WSTYLE)-1 ) )
	{
		msword_direct_prop->style = (struct MsWordStyle *)malloc( sizeof(struct MsWordStyle) ) ;
		if( msword_direct_prop->style == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "malloc failed , errno[%d]" , errno )
			return nret;
		}
		memset( msword_direct_prop->style , 0x00 , sizeof(struct MsWordStyle) );
		
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnStylesXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == FASTERXML_NODE_BRANCH && xpath_len == sizeof(XPATH_WSTYLES_WSTYLE_WNAME)-1 && STRNCMP( xpath , == , XPATH_WSTYLES_WSTYLE_WNAME , sizeof(XPATH_WSTYLES_WSTYLE_WNAME)-1 ) )
	{
		nret = TravelXmlPropertiesBuffer( properties , properties_len , type , xpath , xpath_len , xpath_size , content , content_len , CallbackOnStylesXmlProperty , user ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "TravelXmlPropertiesBuffer failed[%d]" , nret )
			return nret;
		}
	}
	else if( type == (FASTERXML_NODE_LEAVE|FASTERXML_NODE_BRANCH) && xpath_len == sizeof(XPATH_WSTYLES_WSTYLE)-1 && STRNCMP( xpath , == , XPATH_WSTYLES_WSTYLE , sizeof(XPATH_WSTYLES_WSTYLE)-1 ) )
	{
		nret = LinkStylesHashNode_BY_style_id( msword_direct_prop , msword_direct_prop->style ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "LinkStylesHashNode_BY_style_id failed[%d]" , errno )
			DestroyStyle( msword_direct_prop->style ); msword_direct_prop->style = NULL ;
			return nret;
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "LinkStylesHashNode_BY_style_id ok , style_id[%s] style_name[%s]" , msword_direct_prop->style->style_id , msword_direct_prop->style->style_name )
		}
		
		msword_direct_prop->style = NULL ;
	}
	
	return 0;
}

static int ParseStylesFileContent( struct ZlangDirectProperty_msword *msword_direct_prop , char *file_content , uint64_t file_content_len )
{
	char		xpath[ 1024 ] ;
	int		nret = 0 ;
	
	memset( xpath , 0x00 , sizeof(xpath) );
	nret = TravelXmlBuffer( file_content , xpath , sizeof(xpath) , CallbackOnStylesXmlNode , msword_direct_prop ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "TravelXmlBuffer failed[%d]" , nret )
		return nret;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( msword_direct_prop->rt , "TravelXmlBuffer ok" )
		return 0;
	}
}

#define DOCUMENT_FILENAME	"word/document.xml"
#define STYLES_FILENAME		"word/styles.xml"

ZlangInvokeFunction ZlangInvokeFunction_msword_LoadFile_string;
int ZlangInvokeFunction_msword_LoadFile_string( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	unz_global_info				global_info ;
	unz_file_info				file_info ;
	uint64_t				number_entry ;
	int					i ;
	char					filename[ PATH_MAX ] ;
	char					*extname = NULL ;
	char					*buf = NULL ;
	uint64_t				buf_size ;
	uint64_t				buf_len ;
	int					nret = 0 ;
	
	CallRuntimeFunction_string_ExpandEnvVariables( rt , in1 );
	CallRuntimeFunction_string_GetStringValue( rt , in1 , & (msword_direct_prop->msword_filename) , NULL );
	
	msword_direct_prop->unzip_file = unzOpen( msword_direct_prop->msword_filename ) ;
	if( msword_direct_prop->unzip_file == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "unzOpen[%s] failed" , msword_direct_prop->msword_filename )
		msword_direct_prop->msword_filename = NULL ;
		CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
		return 0;
	}
	
	nret = unzGetGlobalInfo( msword_direct_prop->unzip_file , & global_info ) ;
	if( nret != UNZ_OK )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "unzGetGlobalInfo[%s] failed" , msword_direct_prop->msword_filename )
		unzClose( msword_direct_prop->unzip_file );
		msword_direct_prop->msword_filename = NULL ;
		CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
		return 0;
	}
	
	number_entry = global_info.number_entry ;
	
	for( i = 0 ; i < number_entry ; i++ )
	{
		memset( filename , 0x00 , sizeof(filename) );
		nret = unzGetCurrentFileInfo( msword_direct_prop->unzip_file , & file_info , filename , sizeof(filename) , NULL , 0 , NULL , 0 ) ;
		if( nret != UNZ_OK )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "unzGetCurrentFileInfo[%s] failed" , msword_direct_prop->msword_filename )
			if( buf ) free( buf );
			msword_direct_prop->msword_filename = NULL ;
			unzClose( msword_direct_prop->unzip_file );
			CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
			return 0;
		}
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "FILE-IN-ZIP - file_info.external_fa[%lu] filename[%s]" , (unsigned long)(file_info.external_fa) , filename )
		
		extname = strchr( filename , '.' ) ;
		if( extname )
			extname++;
		else
			extname = "" ;
		
		if( STRCMP( filename , == , DOCUMENT_FILENAME ) )
		{
			nret = OpenAndReadAndCloseFileInZip( msword_direct_prop->unzip_file , & file_info , filename , & buf , & buf_size , & buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "OpenAndReadAndCloseFileInZip[%s] failed[%d]" , msword_direct_prop->msword_filename , nret )
				if( buf ) free( buf );
				msword_direct_prop->msword_filename = NULL ;
				unzClose( msword_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
			
			nret = ParseDocumentFileContent( msword_direct_prop , buf , buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ParseDocumentFileContent[%s][%s] failed[%d]" , msword_direct_prop->msword_filename , filename , nret )
				if( buf ) free( buf );
				msword_direct_prop->msword_filename = NULL ;
				unzClose( msword_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
		}
		else if( STRCMP( filename , == , STYLES_FILENAME ) )
		{
			nret = OpenAndReadAndCloseFileInZip( msword_direct_prop->unzip_file , & file_info , filename , & buf , & buf_size , & buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "OpenAndReadAndCloseFileInZip[%s] failed[%d]" , msword_direct_prop->msword_filename , nret )
				if( buf ) free( buf );
				msword_direct_prop->msword_filename = NULL ;
				unzClose( msword_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
			
			nret = ParseStylesFileContent( msword_direct_prop , buf , buf_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ParseStylesFileContent[%s][%s] failed[%d]" , msword_direct_prop->msword_filename , filename , nret )
				if( buf ) free( buf );
				msword_direct_prop->msword_filename = NULL ;
				unzClose( msword_direct_prop->unzip_file );
				CallRuntimeFunction_bool_SetBoolValue( rt , out1 , FALSE );
				return 0;
			}
		}
		
		unzGoToNextFile( msword_direct_prop->unzip_file );
	}
	
	if( buf ) free( buf );
	unzClose( msword_direct_prop->unzip_file ); msword_direct_prop->unzip_file = NULL ;
	CallRuntimeFunction_bool_SetBoolValue( rt , out1 , TRUE );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msword_SetConvertEncoding_string;
int ZlangInvokeFunction_msword_SetConvertEncoding_string( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	char					*convert_encoding = NULL ;
	int32_t					convert_encoding_len ;
	
	CallRuntimeFunction_string_GetStringValue( rt , in1 , & convert_encoding , & convert_encoding_len );
	
	msword_direct_prop->convert_encoding = strndup( convert_encoding , convert_encoding_len ) ;
	if( msword_direct_prop->convert_encoding == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "strndup failed , errno[%d]" , errno )
		UnreferObject( rt , out1 );
		return 0;
	}
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "set output encoding[%s]" , msword_direct_prop->convert_encoding )
	
	CallRuntimeFunction_bool_SetBoolValue( rt , out1 , TRUE );
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msword_GetLineCount;
int ZlangInvokeFunction_msword_GetLineCount( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	int32_t					line_array_size ;
	
	line_array_size = msword_direct_prop->line_count ;
	CallRuntimeFunction_int_SetIntValue( rt , out1 , line_array_size );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msword_GetLineText_int;
int ZlangInvokeFunction_msword_GetLineText_int( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	int32_t					line_idx ;
	struct MsWordLine			*line ;
	
	CallRuntimeFunction_int_GetIntValue( rt , in1 , & line_idx );
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "paragraph line_count[%"PRIi32"] " , line_idx )
	if( line_idx > msword_direct_prop->line_count )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "line_idx[%"PRIi32"] > [%"PRIi32"]" , line_idx , msword_direct_prop->line_count )
		UnreferObject( rt , out1 );
		return 0;
	}
	
	line = msword_direct_prop->line_array+(line_idx-1) ;
	if( line->text == NULL )
	{
		CallRuntimeFunction_string_SetStringValue( rt , out1 , "" , 1 );
	}
	else
	{
		char	*output_text = NULL ;
		size_t	output_text_len ;
		int	nret = 0 ;
		
		if( msword_direct_prop->convert_encoding == NULL || msword_direct_prop->file_encoding == NULL )
		{
			CallRuntimeFunction_string_SetStringValue( rt , out1 , line->text , line->text_len );
		}
		else
		{
			nret = ConvertStringEncoding( msword_direct_prop->file_encoding , msword_direct_prop->convert_encoding , line->text , (size_t)(line->text_len) , & output_text , NULL , & output_text_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ConvertStringEncoding failed[%d] , in[%p] in_len[%"PRIi32"]" , nret , line->text , line->text_len )
				UnreferObject( rt , out1 );
				return 0;
			}
			
			CallRuntimeFunction_string_SetStringValue( rt , out1 , output_text , output_text_len );
			
			free( output_text );
		}
	}
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_msword_GetLineStyle_int;
int ZlangInvokeFunction_msword_GetLineStyle_int( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	int32_t					line_idx ;
	struct MsWordLine			*line ;
	
	CallRuntimeFunction_int_GetIntValue( rt , in1 , & line_idx );
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "paragraph line_count[%"PRIi32"] " , line_idx )
	if( line_idx > msword_direct_prop->line_count )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "line_idx[%"PRIi32"] > [%"PRIi32"]" , line_idx , msword_direct_prop->line_count )
		UnreferObject( rt , out1 );
		return 0;
	}
	
	line = msword_direct_prop->line_array+(line_idx-1) ;
	if( line->style_id == NULL )
	{
		CallRuntimeFunction_string_SetStringValue( rt , out1 , "" , 1 );
	}
	else
	{
		struct MsWordStyle	a_style ;
		struct MsWordStyle	*p_style = NULL ;
		
		memset( & a_style , 0x00 , sizeof(struct MsWordStyle) );
		a_style.style_id = line->style_id ;
		p_style = QueryStylesHashNode_BY_style_id( msword_direct_prop , & a_style ) ;
		if( p_style == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "QueryStylesHashNode_BY_style_id return null" )
			UnreferObject( rt , out1 );
			return 0;
		}
		
		CallRuntimeFunction_string_SetStringValue( rt , out1 , p_style->style_name , -1 );
	}
	
	return 0;
}

ZlangCreateDirectPropertyFunction ZlangCreateDirectProperty_msword;
void *ZlangCreateDirectProperty_msword( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msword	*msword_prop = NULL ;
	
	msword_prop = (struct ZlangDirectProperty_msword *)ZLMALLOC( sizeof(struct ZlangDirectProperty_msword) ) ;
	if( msword_prop == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for entity" )
		return NULL;
	}
	memset( msword_prop , 0x00 , sizeof(struct ZlangDirectProperty_msword) );
	
	msword_prop->rt = rt ;
	
	return msword_prop;
}

ZlangDestroyDirectPropertyFunction ZlangDestroyDirectProperty_msword;
void ZlangDestroyDirectProperty_msword( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_msword	*msword_direct_prop = GetObjectDirectProperty(obj) ;
	uint32_t				n ;
	struct MsWordLine			*line = NULL ;
	
	msword_direct_prop->msword_filename = NULL ;
	
	if( msword_direct_prop->file_encoding )
	{
		free( msword_direct_prop->file_encoding );
	}
	
	if( msword_direct_prop->convert_encoding )
	{
		free( msword_direct_prop->convert_encoding );
	}
	
	if( msword_direct_prop->line_array )
	{
		for( n = 0 , line = msword_direct_prop->line_array ; n < msword_direct_prop->line_count ; n++ , line++ )
		{
			if( line->text )
				free( line->text );
			if( line->style_id )
				free( line->style_id );
		}
		
		free( msword_direct_prop->line_array );
	}
	
	if( msword_direct_prop->styles_hashmap )
	{
		DestroyStylesHashMap( msword_direct_prop );
	}
	
	ZLFREE( msword_direct_prop );
	
	return;
}

ZlangSummarizeDirectPropertySizeFunction ZlangSummarizeDirectPropertySize_msword;
void ZlangSummarizeDirectPropertySize_msword( struct ZlangRuntime *rt , struct ZlangObject *obj , size_t *summarized_obj_size , size_t *summarized_direct_prop_size )
{
	SUMMARIZE_SIZE( summarized_direct_prop_size , sizeof(struct ZlangDirectProperty_msword) )
	return;
}

static struct ZlangDirectFunctions direct_funcs_msword =
	{
		ZLANG_OBJECT_msword , /* char *ancestor_name */
		
		ZlangCreateDirectProperty_msword , /* ZlangCreateDirectPropertyFunction *create_entity_func */
		ZlangDestroyDirectProperty_msword , /* ZlangDestroyDirectPropertyFunction *destroy_entity_func */
		
		NULL , /* ZlangFromCharPtrFunction *from_char_ptr_func */
		NULL , /* ZlangToStringFunction *to_string_func */
		NULL , /* ZlangFromDataPtrFunction *from_data_ptr_func */
		NULL , /* ZlangGetDataPtrFunction *get_data_ptr_func */
		
		NULL , /* ZlangOperatorFunction *oper_PLUS_func */
		NULL , /* ZlangOperatorFunction *oper_MINUS_func */
		NULL , /* ZlangOperatorFunction *oper_MUL_func */
		NULL , /* ZlangOperatorFunction *oper_DIV_func */
		NULL , /* ZlangOperatorFunction *oper_MOD_func */
		
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_NEGATIVE_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_NOT_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_BIT_REVERSE_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_PLUS_PLUS_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_MINUS_MINUS_func */
		
		NULL , /* ZlangCompareFunction *comp_EGUAL_func */
		NULL , /* ZlangCompareFunction *comp_NOTEGUAL_func */
		NULL , /* ZlangCompareFunction *comp_LT_func */
		NULL , /* ZlangCompareFunction *comp_LE_func */
		NULL , /* ZlangCompareFunction *comp_GT_func */
		NULL , /* ZlangCompareFunction *comp_GE_func */
		
		NULL , /* ZlangLogicFunction *logic_AND_func */
		NULL , /* ZlangLogicFunction *logic_OR_func */
		
		NULL , /* ZlangLogicFunction *bit_AND_func */
		NULL , /* ZlangLogicFunction *bit_XOR_func */
		NULL , /* ZlangLogicFunction *bit_OR_func */
		NULL , /* ZlangLogicFunction *bit_MOVELEFT_func */
		NULL , /* ZlangLogicFunction *bit_MOVERIGHT_func */
		
		ZlangSummarizeDirectPropertySize_msword , /* ZlangSummarizeDirectPropertySizeFunction *summarize_direct_prop_size_func */
	} ;

ZlangImportObjectFunction ZlangImportObject_msword;
struct ZlangObject *ZlangImportObject_msword( struct ZlangRuntime *rt )
{
	struct ZlangObject	*obj = NULL ;
	struct ZlangFunction	*func = NULL ;
	int			nret = 0 ;
	
	nret = ImportObject( rt , & obj , ZLANG_OBJECT_msword , & direct_funcs_msword , sizeof(struct ZlangDirectFunctions) , NULL ) ;
	if( nret )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_LINK_FUNC_TO_ENTITY , "import object to global objects heap" )
		return NULL;
	}
	
	/* msword.LoadFile(string) */
	func = AddFunctionAndParametersInObject( rt , obj , "LoadFile" , "LoadFile(string)" , ZlangInvokeFunction_msword_LoadFile_string , ZLANG_OBJECT_bool , ZLANG_OBJECT_string,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msword.SetConvertEncoding(string) */
	func = AddFunctionAndParametersInObject( rt , obj , "SetConvertEncoding" , "SetConvertEncoding(string)" , ZlangInvokeFunction_msword_SetConvertEncoding_string , ZLANG_OBJECT_bool , ZLANG_OBJECT_string,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msword.GetLineCount() */
	func = AddFunctionAndParametersInObject( rt , obj , "GetLineCount" , "GetLineCount()" , ZlangInvokeFunction_msword_GetLineCount , ZLANG_OBJECT_int , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msword.GetLineText(int) */
	func = AddFunctionAndParametersInObject( rt , obj , "GetLineText" , "GetLineText(int)" , ZlangInvokeFunction_msword_GetLineText_int , ZLANG_OBJECT_string , ZLANG_OBJECT_int,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* msword.GetLineStyle(int) */
	func = AddFunctionAndParametersInObject( rt , obj , "GetLineStyle" , "GetLineStyle(int)" , ZlangInvokeFunction_msword_GetLineStyle_int , ZLANG_OBJECT_string , ZLANG_OBJECT_int,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	return obj ;
}

